/*
 * Copyright 2010 Google Inc.
 *
 * Use of this source code is governed by a BSD-style license that can be
 * found in the LICENSE file.
 */

#ifndef SkRasterClip_DEFINED
#define SkRasterClip_DEFINED

#include "include/core/SkClipOp.h"
#include "include/core/SkRegion.h"
#include "include/core/SkShader.h"
#include "include/private/base/SkMacros.h"
#include "src/core/SkAAClip.h"

class SkRRect;

/* *
 * Wraps a SkRegion and SkAAClip, so we have a single object that can represent either our
 * BW or antialiased clips.
 */
class SkRasterClip {
public:
    SkRasterClip();
    explicit SkRasterClip(const SkIRect &);
    explicit SkRasterClip(const SkRegion &);
    explicit SkRasterClip(const SkRasterClip &);
    SkRasterClip(const SkPath &path, const SkIRect &bounds, bool doAA);

    ~SkRasterClip();

    SkRasterClip &operator = (const SkRasterClip &);

    bool isBW() const
    {
        return fIsBW;
    }
    bool isAA() const
    {
        return !fIsBW;
    }
    const SkRegion &bwRgn() const
    {
        SkASSERT(fIsBW);
        return fBW;
    }
    const SkAAClip &aaRgn() const
    {
        SkASSERT(!fIsBW);
        return fAA;
    }

    bool isEmpty() const
    {
        SkASSERT(this->computeIsEmpty() == fIsEmpty);
        return fIsEmpty;
    }

    bool isRect() const
    {
        SkASSERT(this->computeIsRect() == fIsRect);
        return fIsRect;
    }

    bool isComplex() const
    {
        return fIsBW ? fBW.isComplex() : !fAA.isEmpty();
    }
    const SkIRect &getBounds() const
    {
        return fIsBW ? fBW.getBounds() : fAA.getBounds();
    }

    bool setEmpty();
    bool setRect(const SkIRect &);

    bool op(const SkIRect &, SkClipOp);
    bool op(const SkRegion &, SkClipOp);
    bool op(const SkRect &, const SkMatrix &matrix, SkClipOp, bool doAA);
    bool op(const SkRRect &, const SkMatrix &matrix, SkClipOp, bool doAA);
    bool op(const SkPath &, const SkMatrix &matrix, SkClipOp, bool doAA);
    bool op(sk_sp<SkShader>);

    void translate(int dx, int dy, SkRasterClip *dst) const;

    bool quickContains(const SkIRect &rect) const
    {
        return fIsBW ? fBW.quickContains(rect) : fAA.quickContains(rect);
    }

    /* *
     * Return true if this region is empty, or if the specified rectangle does
     * not intersect the region. Returning false is not a guarantee that they
     * intersect, but returning true is a guarantee that they do not.
     */
    bool quickReject(const SkIRect &rect) const
    {
        return !SkIRect::Intersects(this->getBounds(), rect);
    }

#ifdef SK_DEBUG
    void validate() const;
#else
    void validate() const {}
#endif

    sk_sp<SkShader> clipShader() const
    {
        return fShader;
    }

private:
    SkRegion fBW;
    SkAAClip fAA;
    bool fIsBW;
    // these 2 are caches based on querying the right obj based on fIsBW
    bool fIsEmpty;
    bool fIsRect;
    // if present, this augments the clip, not replaces it
    sk_sp<SkShader> fShader;

    bool computeIsEmpty() const
    {
        return fIsBW ? fBW.isEmpty() : fAA.isEmpty();
    }

    bool computeIsRect() const
    {
        return fIsBW ? fBW.isRect() : fAA.isRect();
    }

    bool updateCacheAndReturnNonEmpty(bool detectAARect = true)
    {
        fIsEmpty = this->computeIsEmpty();

        // detect that our computed AA is really just a (hard-edged) rect
        if (detectAARect && !fIsEmpty && !fIsBW && fAA.isRect()) {
            fBW.setRect(fAA.getBounds());
            fAA.setEmpty(); // don't need this anymore
            fIsBW = true;
        }

        fIsRect = this->computeIsRect();
        return !fIsEmpty;
    }

    void convertToAA();

    bool op(const SkRasterClip &, SkClipOp);
};

class SkAutoRasterClipValidate : SkNoncopyable {
public:
    SkAutoRasterClipValidate(const SkRasterClip &rc) : fRC(rc)
    {
        fRC.validate();
    }
    ~SkAutoRasterClipValidate()
    {
        fRC.validate();
    }

private:
    const SkRasterClip &fRC;
};

#ifdef SK_DEBUG
#define AUTO_RASTERCLIP_VALIDATE(rc) SkAutoRasterClipValidate arcv(rc)
#else
#define AUTO_RASTERCLIP_VALIDATE(rc)
#endif

// /////////////////////////////////////////////////////////////////////////////

/* *
 * Encapsulates the logic of deciding if we need to change/wrap the blitter
 * for aaclipping. If so, getRgn and getBlitter return modified values. If
 * not, they return the raw blitter and (bw) clip region.
 *
 * We need to keep the constructor/destructor cost as small as possible, so we
 * can freely put this on the stack, and not pay too much for the case when
 * we're really BW anyways.
 */
class SkAAClipBlitterWrapper {
public:
    SkAAClipBlitterWrapper();
    SkAAClipBlitterWrapper(const SkRasterClip &, SkBlitter *);
    SkAAClipBlitterWrapper(const SkAAClip *, SkBlitter *);

    void init(const SkRasterClip &, SkBlitter *);

    const SkIRect &getBounds() const
    {
        SkASSERT(fClipRgn);
        return fClipRgn->getBounds();
    }
    const SkRegion &getRgn() const
    {
        SkASSERT(fClipRgn);
        return *fClipRgn;
    }
    SkBlitter *getBlitter()
    {
        SkASSERT(fBlitter);
        return fBlitter;
    }

private:
    SkRegion fBWRgn;
    SkAAClipBlitter fAABlitter;
    // what we return
    const SkRegion *fClipRgn;
    SkBlitter *fBlitter;
};

#endif
